import React, { useRef, useCallback, useEffect, useState } from 'react';
import ReactFlow, {
  // useReactFlow,
  useNodesState,
  useEdgesState,
  addEdge,
  Controls,
  Background,
  // MiniMap,
  MarkerType,
} from 'reactflow';
import cls from 'classnames';
import { isNil, isEmpty, merge } from 'lodash-es';
import { nanoid } from 'nanoid';
// import { proxy, useSnapshot, subscribe } from 'valtio'
import { subscribe } from '@umijs/max';

import type { DragEvent } from 'react';
import type {
  ReactFlowInstance,
  Connection,
  OnConnectStartParams,
  Node,
  // BackgroundVariant
} from 'reactflow';

import { CardNode } from '@/components';
import {
  RelationNode,
  LinkEdge,
  ResizableRoundRectangle,
  ResizableParallelogram,
  ResizableHexagon,
  ResizableArrowRectangle,
  ResizableDiamond,
  ResizableDatabase,
  ResizableTriangle,
  ResizableEllipse,
  ResizableCircle,
} from '../components';

import { useFlowContext } from '../context';

// 自定义节点
const nodeTypes = {
  ResizableRoundRectangle, // 矩形
  ResizableParallelogram, // 平行四边形
  ResizableHexagon, // 多边形
  ResizableArrowRectangle, // 箭头矩形
  ResizableDiamond, // 菱形
  ResizableDatabase, // 圆柱形
  ResizableTriangle, // 三角形
  ResizableEllipse, // 椭圆
  ResizableCircle, // 圆形
  RelationNode,
  CardNode,
};

// 自定义连线
const edgeTypes = {
  link: LinkEdge,
};

const fitViewOptions = {
  padding: .5,
  // minZoom: 0,
  // maxZoom: 1,
};

/**
 * 初始化通过 onInit 回调得到 ReactFlow 实例
 * 接着处理 onDragOver 事件，更新 dropEffect，和 effectAllowed 保持一致
 * 然后在 onDrop 事件处理函数中，通过 getBoundingClientRect 获取画布容器的坐标信息
 * 但坐标信息需要通过 ReactFlow 实例提供的 project 方法处理为 ReactFlow 坐标系
 * 最后组装节点信息，更新 nodes, edges
 */
type IProps = {
  schema: object;
  instance: ReactFlowInstance | null;
  setInstance: (obj: ReactFlowInstance) => void;
  fitView?: boolean;
  readOnly?: boolean;
  theme?: string;
  control?: boolean;
  className?: string;
}

const FlowGraph: React.FC<IProps> = (props) => {
  const {
    schema,
    instance,
    setInstance,
    fitView = false,
    readOnly = false,
    theme,
    control = true,
    className,
  } = props;

  const isDarkTheme = theme === 'dark';

  // 画布的 DOM 容器，用于计算节点坐标
  const graphRef = useRef<HTMLDivElement | null>(null);
  const connectingNodeId = useRef<string | null>(null);

  // node节点、edge连线
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);

  // @ts-ignore
  const [state, setState ]= useState(useFlowContext());

  useEffect(() => {
    const unsubscribe = subscribe(state, () => {
      console.log('sub', state)
      setState(prevState => ({
        ...prevState,
        backgroundProps: state.backgroundProps,
      }));
    });
    return () => unsubscribe()
  }, []);

  // 预览状态，设置节点
  useEffect(() => {
    if (isEmpty(schema)) return;
    // const { nodes, edges, viewport } = schema;
    // @ts-ignore
    let { nodes, edges } = schema;

    // readOnly只读状态，禁止拖拽
    if (readOnly) {
      nodes = nodes.map((it: Node) => {
        const { width, height, data } = it;
        return merge(it, {
          draggable: false, // 禁止拖拽 nodes
          readOnly: true,
          data: merge(data, { width, height, readOnly: true }),
        });
      });
    }

    setNodes(nds => nds.concat(nodes));
    setEdges(eds => eds.concat(edges));
  }, [schema, setNodes, setEdges]);

  // 画布加载完毕，保存当前 ReactFlow画布实例
  const onInit = useCallback((reactFlowInstance: ReactFlowInstance) => {
    // console.log(50, reactFlowInstance.toObject());
    setInstance(reactFlowInstance);
  }, [setInstance]);

  /**
   * 拖拽完成后放置节点
   * @param e
   */
  function onDrop(e: DragEvent<HTMLDivElement>) {
    e.preventDefault();
    e.stopPropagation();

    // 节点类型
    const itemString = e.dataTransfer.getData('application/reactflow');
    const item = JSON.parse(itemString);
    // console.log(98, item, e.dataTransfer);

    if (isNil(graphRef.current) || isNil(instance) || !item.type) {
      return;
    }

    // 获取画布 DOM容器
    const rect = graphRef.current.getBoundingClientRect();

    /**
     * 获取松开时的坐标
     * 用 project 将像素坐标转换为内部 ReactFlow 坐标系
     */
    const position = instance.project({
      x: e.clientX - rect.left,
      y: e.clientY - rect.top,
    });

    const _nodes = {
      ...item,
      id: nanoid(),
      position,
      draggable: true,
      data: {
        ...item.data,
        width: 120,
        height: 50,
      },
    };
    // 保存节点配置信息
    setNodes(nds => nds.concat(_nodes));
    console.log('onDrop', _nodes);
  }

  /**
   * 正在拖动到放置目标时，高频，会一直触发
   * const { run } = useDebounceFn(onDragOver, {wait: 50});
   * @param e {React.DragEvent<HTMLDivElement>}
   */
  function onDragOver(e: DragEvent<HTMLDivElement>) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
    // console.log('onDragOver', e)
  }

  /**
   * 画布上连线的时，触发 onConnect 事件，并提供连线信息
   * 然后通过 addEdge 来添加连线，addEdge接收两个参数 edgeParams 和 edges，最后返回全新的 edges
   * addEdge(edgeParams: Edge, edges: Edge[]): Edge[]
   * @param edgeParams { Connection }
   */
  const onConnect = useCallback((params: Connection) => {
    const _edge = {
      ...params,
      type: 'smoothstep',
      markerEnd: {
        type: MarkerType.ArrowClosed,
      },
      // sourceHandle: params.source
    };
    console.log(202, _edge, params);
    setEdges((eds) => addEdge(_edge, eds));
  }, [setEdges]);

  const onConnectStart = useCallback((e: any, { nodeId }: OnConnectStartParams) => {
    connectingNodeId.current = nodeId;
  }, []);

  function onNodeClick(e: any, node: any) {
    console.log(135, node, 'nodes', nodes);
    // console.log(135, JSON.stringify(instance?.toObject()));
  }
  console.log(JSON.stringify(state.backgroundProps))

  return (
    <div
      className='flex-1 h-full'
      ref={graphRef}
    >
      <ReactFlow
        nodes={nodes}
        edges={edges}
        nodeTypes={nodeTypes} // 自定义节点和边
        edgeTypes={edgeTypes}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onInit={onInit}
        // 拖拽
        onDragOver={onDragOver}
        onDrop={onDrop}
        onNodeClick={onNodeClick}

        // 画布上连线的时，触发 onConnect，并提供连线信息 source, target
        onConnect={onConnect}
        onConnectStart={onConnectStart}
        // onConnectEnd={onConnectEnd}
        // panOnScroll
        // 节点自适应画布，会自定缩放 zoom: 2
        fitView={fitView}
        fitViewOptions={fitViewOptions}
        className={cls(className, { dark: isDarkTheme })}
        // defaultViewport={{ x: 0, y: 0, zoom: 1.5 }}
      >
        {control && <Controls /> }
        <Background
          {...state.backgroundProps}
        />
      </ReactFlow>
    </div>
  );
};

export default FlowGraph;
